/*
 * DeltaCoder.cpp
 *
 *  Created on: Nov 7, 2010
 *      Author: lucianoml83
 */

#include "DeltaCoder.h"

DeltaCoder::DeltaCoder() {
}

string DeltaCoder::eliasGammaEncode(int source) {
  double logSource = log(source);
  double logDos = log(2);
  double divisionLogs = logSource / logDos;
  int numberBits = (int)divisionLogs;
  string bstring = this->toBinaryString(source);
  for (int i = numberBits - 1; i >= 0; i--) {
    bstring = "0" + bstring;
  }
  return bstring;
}

string DeltaCoder::eliasDeltaEncode(int source) {
  int near = this->nearPot(source);
  string delta = this->eliasGammaEncode(near + 1);
  double valorRestante = source - pow(2, near);
  string parteBinaria = this->toBinaryString((int)valorRestante);
  string appendeo = "";
  int resta = near - parteBinaria.size();
  for (int i = 0; i < resta; i++) {
    appendeo += "0";
  }
  if (near != 0)
    appendeo += parteBinaria;
  return delta + appendeo;
}

int DeltaCoder::eliasDeltaDecode(string source) {
  int cuentaDeCeros = 0;
  for (unsigned int i = 0; i < source.size(); i++) {
    if (source.at(i) == '0') {
      cuentaDeCeros++;
    } else break;
  }
  string substrX = source.substr(cuentaDeCeros, cuentaDeCeros + 1);
  int indiceBusq = (2*cuentaDeCeros) + 1;
  string substrY = source.substr(indiceBusq, source.size() - indiceBusq);

  int intX = this->toInteger(substrX);
  int intY = 0;
  if (substrY.compare("") != 0)
    intY = this->toInteger(substrY);

  double calculo = pow(2, intX-1) + intY;

  return (int)calculo;
}

int DeltaCoder::nearPot(int number) {
  double value = log(number) / log(2);
  int pot = (int)value;
  return pot;
}

string DeltaCoder::toBinaryString(unsigned int input) {
  string result;
  do result.push_back('0' + (input & 1));
    while (input >>= 1);
  reverse(result.begin(), result.end());
  return result;
}

int DeltaCoder::toInteger(string input) {
  return (int)bitset<32>(input).to_ulong();
}

void DeltaCoder::pruebaDelta() {
  cout << "Prueba para demostrar que da igual que en el apunte." << endl;
  cout << "----------------------------------------------------" << endl;
  for (int i = 1; i < 1024; i++) {
    string delta = this->eliasDeltaEncode(i);
    cout << "N = " << i << "; Delta(N) = " << delta << "; DecodeoDelta(" <<
        delta << ") = " << this->eliasDeltaDecode(delta) << endl;
  }
}

DeltaCoder::~DeltaCoder() {
}
